using System;
using LiteNetLib.Utils;

namespace LiteNetLib
{
	internal sealed class NetPacket
	{
		private const int LastProperty = 24;

		public byte[] RawData;

		public int Size;

		public PacketProperty Property
		{
			get
			{
				return (PacketProperty)(RawData[0] & 0x1F);
			}
			set
			{
				RawData[0] = (byte)((uint)(RawData[0] & 0xE0) | (uint)value);
			}
		}

		public byte ConnectionNumber
		{
			get
			{
				return (byte)((RawData[0] & 0x60) >> 5);
			}
			set
			{
				RawData[0] = (byte)((RawData[0] & 0x9F) | (value << 5));
			}
		}

		public ushort Sequence
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 1);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 1, value);
			}
		}

		public bool IsFragmented
		{
			get
			{
				return (RawData[0] & 0x80) != 0;
			}
		}

		public ushort FragmentId
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 3);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 3, value);
			}
		}

		public ushort FragmentPart
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 5);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 5, value);
			}
		}

		public ushort FragmentsTotal
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 7);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 7, value);
			}
		}

		public void MarkFragmented()
		{
			RawData[0] |= 128;
		}

		public NetPacket(int size)
		{
			RawData = new byte[size];
			Size = size;
		}

		public NetPacket(PacketProperty property, int size)
		{
			size += GetHeaderSize(property);
			RawData = new byte[size];
			Property = property;
			Size = size;
		}

		public void Realloc(int toSize, bool clear)
		{
			Size = toSize;
			if (RawData.Length < toSize)
			{
				RawData = new byte[toSize];
			}
			else if (clear)
			{
				Array.Clear(RawData, 0, toSize);
			}
		}

		public static int GetHeaderSize(PacketProperty property)
		{
			switch (property)
			{
			case PacketProperty.ReliableUnordered:
			case PacketProperty.Sequenced:
			case PacketProperty.ReliableOrdered:
			case PacketProperty.AckReliable:
			case PacketProperty.AckReliableOrdered:
			case PacketProperty.Ping:
			case PacketProperty.ReliableSequenced:
			case PacketProperty.AckReliableSequenced:
				return 3;
			case PacketProperty.ConnectRequest:
				return 13;
			case PacketProperty.ConnectAccept:
				return 11;
			case PacketProperty.Disconnect:
				return 9;
			case PacketProperty.Pong:
				return 11;
			default:
				return 1;
			}
		}

		public int GetHeaderSize()
		{
			return GetHeaderSize(Property);
		}

		public bool FromBytes(byte[] data, int start, int packetSize)
		{
			byte num = (byte)(data[start] & 0x1F);
			bool flag = (data[start] & 0x80) != 0;
			int headerSize = GetHeaderSize((PacketProperty)num);
			if (num > 24 || packetSize < headerSize || (flag && packetSize < headerSize + 6))
			{
				return false;
			}
			Buffer.BlockCopy(data, start, RawData, 0, packetSize);
			Size = packetSize;
			return true;
		}
	}
}
